package com.linwen.javase.new8.stream;

import org.junit.Before;
import org.junit.Test;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * 使用stream api可以对集合数据进行操作，可以使得代码干净，简洁
 *
 * Stream和Collection的区别：
 *  Collection 内存打交道，主要涉及数据的存储
 *  Stream  跟cpu打交道，主要是计算，不改变原来存储的数据，将返回新的stream对象数据
 * */
public class StreamTest {
    ArrayList<StreamObj> list = new ArrayList<>();
    /**
     * 流的使用注意事项
     * 1 惰性求值，只有进行终结操作之后流才开始执行
     * 2 流是一次性的，进行终结操作之后就会关闭
     * 3 不会影响原来的集合数据
     * */
    @Test
    public void test(){
        /**
         * stram流的使用案例
         * stream流的创建
         * stream流中间操作
         * stream流终结操作
         * */
        List<Integer> list = Arrays.asList(1, 2, 3,3,3,1, 4, 5, 6, 7, 8, 9);
        list.stream().distinct()
                .filter(integer -> integer>5)
                .forEach(System.out::println);

    }

    /**
     * 流的创建
     * */
    @Test
    public void test1(){
        // 单列集合 Collections
        // 双列集合 Map
        // 数组
        ArrayList<Object> list = new ArrayList<>();
        Stream<Object> listStream = list.stream();

        int[] array = new int[]{1,2,3,4,5};
        IntStream arrStrram = Arrays.stream(array);
        Stream<int[]> arrayStream = Stream.of(array);

        HashMap<String, Integer> map = new HashMap<>();
        map.put("zhangsan",1);
        map.put("lisi",4);
        map.put("wangwu",6);
        Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
        stream.filter(entry -> entry.getValue()>3)
                .forEach(entry -> System.out.println(entry.getKey()+"  "+entry.getValue()));
    }
    /**
     * 流的中间操作
     * */
    @Test
    public void test2(){

        System.out.println("====filter====");
        // filter  过滤，返回true的元素留下来   过滤年龄大于14的对西那个
        list.stream()
                .filter(obj -> obj.getAge()>14)
                .forEach(System.out::println);
        System.out.println("====map====");
        // map 转换或计算  转换为String流再输出
        list.stream()
                .map(StreamObj::getName)
                .forEach(System.out::println);
        // 对年龄计算后新建对象
        list.stream()
                .map(obj -> StreamObj.builder()
                        .name(obj.getName())
                        .age(obj.getAge()+10)
                        .address(obj.getAddress())
                        .build())
                .forEach(System.out::println);
        System.out.println("====distinct====");
        // distinct 去重   依赖对象的equals
        list.stream()
                .distinct()
                .forEach(System.out::println);
        System.out.println("====sorted====");
        // sorted 排序
        list.stream()
                .sorted(Comparator.comparingInt(StreamObj::getAge))
                .forEach(System.out::println);
        System.out.println("====limit====");
        // limit 截取
        list.stream()
                .limit(2)
                .forEach(System.out::println);
        System.out.println("====skip====");
        // skip 跳过前n个元素
        list.stream()
                .skip(2)
                .forEach(System.out::println);
        System.out.println("====flatMap====");
        // flatMap 流对象中对象作为流   平铺展开
        list.stream()
                .flatMap(obj -> Arrays.stream(obj.getAddress().split(",")))
                .distinct()
                .forEach(System.out::println);
    }

    /**
     * 终结操作
     * */
    @Test
    public void test3(){


        System.out.println("====foreach====");
        // foreach 消费每一个元素
        list.stream()
                .forEach(System.out::println);
        System.out.println("====count====");
        // count 当前stream的元素个数
        long count = list.stream()
                .count();
        System.out.println(count);
        System.out.println("====max====");
        // max 获取最大的年龄
        Optional<Integer> max = list.stream()
                .map(obj -> obj.getAge())
                .max(Comparator.comparingInt(o -> o));
        System.out.println(max.get());
        System.out.println("====min====");
        // min 获取最小的年龄
        Optional<Integer> min = list.stream()
                .map(obj -> obj.getAge())
                .min(Comparator.comparingInt(o -> o));
        System.out.println(min.get());
        System.out.println("====collect====");
        // collect toList 获取名字的集合
        List<String> collect = list.stream()
                .map(obj -> obj.getName())
                .collect(Collectors.toList());
        System.out.println(collect);
        // collect toSet 获取名字的set集合
        Set<String> set = list.stream()
                .map(obj -> obj.getName())
                .collect(Collectors.toSet());
        System.out.println(set);
        // collect toMap 获取名字作为key的map
        Map<String, String> map = list.stream()
                .distinct()
                .collect(Collectors.toMap(StreamObj::getName, StreamObj::getAddress));
        System.out.println(map);
        System.out.println("====anyMatch====");
        // anyMatch 有任意一个满足
        boolean anyMatch = list.stream()
                .anyMatch(obj -> obj.getAge() > 15);
        System.out.println(anyMatch);
        System.out.println("====allMatch====");
        // allMatch 所有元素都满足
        boolean allMatch = list.stream()
                .allMatch(obj -> obj.getAge() > 14);
        System.out.println(allMatch);
        System.out.println("====noneMatch====");
        // noneMatch 所有元素都不满足
        boolean noneMatch = list.stream()
                .noneMatch(obj -> obj.getAge() > 16);
        System.out.println(noneMatch);
        System.out.println("====findAny====");
        // findAny 流中任意一个满足的元素，不保证顺序
        Optional<StreamObj> any = list.stream()
                .findAny();
        System.out.println(any.get());
        System.out.println("====findFirst====");
        // findFirst 流中第一个元素
        Optional<StreamObj> first = list.stream()
                .findFirst();
        System.out.println(first);
        System.out.println("====reduce====");
        // reduce 归并计算,相当于下面代码   所有年龄的和
        /**
         *        int res1 = 0;
         *         for(StreamObj obj:list){
         *             res1 = res1+obj.getAge();
         *         }
         *         return res1;
         * */
        Integer reslut = list.stream()
                .map(obj -> obj.getAge())
                //  初始值，+操作
                .reduce(0, (result, ele) -> result + ele);
        System.out.println(reslut);
        /**
         *  boolean foundAny = false;
         *  T result = null;
         *  for (T element : this stream) {
         *      if (!foundAny) {
         *          foundAny = true;
         *          result = element;
         *         }else
         *       result = accumulator.apply(result, element);
         *  }
         *  return foundAny ? Optional.of(result) : Optional.empty();
         * */
        // reduce 求最大的年龄,单个参数重载，相当于第一个元素设为初始值
        Integer reduce = list.stream()
                .map(obj -> obj.getAge())
                .reduce((res, ele) -> res < ele ? ele : res).get();
        System.out.println(reduce);
    }

    /**
     * 并行流
     * parallelStream默认使用了fork-join框架，其默认线程数是CPU核心数。
     * System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "64");
     * IntStream range = IntStream.range(1, 100000);
     * // 传入parallelism
     * new ForkJoinPool(parallelism).submit(() -> range.parallel().forEach(System.out::println)).get();
     * 最后列举一些使用parallelStream需要注意的地方：
     *
     * 1.parallelStream线程不安全问题（加锁、使用线程安全的集合或者集合采用collect()或reduce()操作就是满足线程安全的；
     *
     * 2.parallelStream 适用的场景是CPU密集型的，假如本身电脑CPU的负载很大，那还到处用并行流，那并不能起到作用，切记不要再paralelSreram操作是中使用IO流；
     *
     * 3.不要在多线程中使用parallelStream，原因同上类似，大家都抢着CPU是没有提升效果，反而还会加大线程切换开销；
     * */
    @Test
    public void test4(){
        System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "2");
        /**
         * 并行流的创建十分简单，parallel()或者parallelStream()，避免了自己写多线程代码
         * */

        list.stream().parallel()
                .peek(obj -> System.out.println(obj+"  "+Thread.currentThread().getName()))
                .map(item->item.getName())
                .collect(Collectors.toList());

        list.parallelStream()
                .peek(obj -> System.out.println(obj+"  "+Thread.currentThread().getName()))
                .map(item->item.getName())
                .collect(Collectors.toList());



    }

    @Before
    public void before(){
        list.add(new StreamObj("张三", 13, "深圳"));
        list.add(new StreamObj("李四",15,"上海"));
        list.add(new StreamObj("王武",14,"北京,上海"));
        list.add(new StreamObj("赵六",16,"武威,南京"));
        list.add(new StreamObj("赵六",16,"武威,南宁"));
    }

}

